package com.alm.handle.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alm.common.core.redis.RedisCache;
import com.alm.common.utils.SecurityUtils;
import com.alm.entity.DevLogin;
import com.alm.entity.DevMeterCurrent;
import com.alm.entity.PasingByteModel;
import com.alm.handle.core.domain.entity.*;
import com.alm.handle.core.domain.entity.SysDevCurrentData_s;
import com.alm.handle.core.domain.entity.rq.DevInfoMacRq;
import com.alm.handle.core.domain.entity.rq.SysDevCurrentDataRq;
import com.alm.handle.core.domain.entity.rs.HomePageChartRS;
import com.alm.handle.core.domain.entity.rs.SysDevCurrentDataRS;
import com.alm.handle.core.domain.entity.vo.CurrentVo;
import com.alm.handle.mapper.*;
import com.alm.handle.mapper.SysDevCurrentData_sMapper;
import com.alm.handle.service.ISysAreaInfoService;
import com.alm.handle.service.ISysDevCurrentDataService;
import com.alm.handle.util.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.util.StringUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.alm.common.utils.PageUtils.startPage;

@Service
public class SysDevCurrentDataServiceImpl implements ISysDevCurrentDataService {
    private static final Logger log = LoggerFactory.getLogger(SysDevCurrentDataServiceImpl.class);
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private SysDevCurrentDataMapper sysDevCurrentDataMapper;
    @Resource
    private SysDevInfoMapper sysDevInfoMapper;
    @Resource
    private SysDevCurrentData_sMapper data_sMapper;
    @Resource
    private SysAreaInfoMapper areaInfoMapper;
    @Autowired
    private ISysAreaInfoService areaInfoService;
    @Resource
    private AlmAutoEneraHisMapper eneraHisMapper;

    @Override
    public List<SysDevCurrentData> selectDevList(SysDevCurrentData devCurrentData) {
         return null;
    }

    @Override
    public List<SysDevCurrentData> selectDevList() {
        return sysDevCurrentDataMapper.selectList(new QueryWrapper<>());
    }

    @Override
    public int insertDevCurrent(PasingByteModel.Header header, DevLogin devLogin) {
        SysDevCurrentData currentData = new SysDevCurrentData();
        currentData.setDeviceId(header.getDvcId());
        currentData.setPowerId(devLogin.getDevPowerId());
        currentData.setTotalEnergyLast(new BigDecimal(0));
        currentData.setTotalEnergy(new BigDecimal(0));
        currentData.setNetworkType(devLogin.getNetWorkType()==null?1:devLogin.getNetWorkType());
        return sysDevCurrentDataMapper.insert(currentData);
    }

    @Override
    public CurrentVo upDateCurrent(DevMeterCurrent current) {
        String redisDataKey = SoketEleKey.ELE_DATA + current.getDeviceId();
        String redisData = redisCache.getCacheObject(redisDataKey);
        CurrentVo currentVo;
        currentVo = ELeCurrentUtil.replaceData(current);
        if (!StringUtil.isEmpty(redisData)) {
            CurrentVo oldcurrentVo = JSON.parseObject(redisData, CurrentVo.class);
            currentVo = ELeCurrentUtil.resetData(oldcurrentVo, currentVo);
        }
        redisCache.setCacheObject(redisDataKey, JSON.toJSONString(currentVo));
        //插入历史记录
        return currentVo;
    }

    @Override
    public boolean updataSument(String devMac, Integer prowId) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("device_id", devMac);
        SysDevCurrentData currentData = sysDevCurrentDataMapper.selectOne(queryWrapper);
        if (currentData == null) {
            log.info("更新历史用电量失败~ 未找到");
            return false;
        }
        if ((prowId != null && currentData.getPowerId() != null) &&
                (prowId == 0 || !currentData.getPowerId().equals(prowId))) {
            BigDecimal sumEle = new BigDecimal(0);
            currentData.setTotalEnergyLast(currentData.getEnergy());
            if (currentData.getTotalEnergy() == null) {
                sumEle = sumEle.add(currentData.getEnergy() == null ? new BigDecimal(0) : currentData.getEnergy());
            } else {
                sumEle = currentData.getTotalEnergy();
                sumEle = sumEle.add(currentData.getEnergy() == null ? new BigDecimal(0) : currentData.getEnergy());
            }
            currentData.setTotalEnergy(sumEle.setScale(5, BigDecimal.ROUND_UP));
            currentData.setPowerId(prowId);
            currentData.setEnergy(new BigDecimal(0.0));
            sysDevCurrentDataMapper.update(currentData, queryWrapper);
            //删除历史记录
            redisCache.deleteCacheMapValue(SoketEleKey.HIS_ENERY, devMac);
            return true;
        }
        return false;
    }



    @Override
    public void updateLineNum(String devmac, Timer timer) {
        SysDevCurrentData devCurrentData = new SysDevCurrentData();
        String devKey = SoketEleKey.ELE_DATA + devmac;
        if (redisCache.hasKey(devKey)) {
            //单路
            devCurrentData.setLineNum(1);
        }
        devKey = SoketEleKey.ELES_DATA + devmac;
        if (redisCache.hasKey(devKey)) {
            //多路
            String redisData = redisCache.getCacheObject(devKey);
            if (!StringUtil.isEmpty(redisData)) {
                Map map = JSON.parseObject(redisData, Map.class);
                devCurrentData.setLineNum(map.size());
            }
        }
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("device_id", devmac);
        devCurrentData.setModifyTime(System.currentTimeMillis());
        sysDevCurrentDataMapper.update(devCurrentData, queryWrapper);
        timer.cancel();
    }

    @Override
    public int updateByNetWorkType(Integer netWork, String devid) {
        SysDevCurrentData sysDevCurrentData=new SysDevCurrentData();
        sysDevCurrentData.setDeviceId(devid);
        sysDevCurrentData.setNetworkType(netWork==null?1:netWork);
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("device_id", devid);
        return  sysDevCurrentDataMapper.update(sysDevCurrentData, queryWrapper);
    }

    @Override
    public void flushPersistenceAll() {
        Collection<String> keys = redisCache.keys(SoketEleKey.ELE_DATA + "*");
        for (String key : keys) {
            flushPersistenceByKey(key);
        }
        //多路更新
        Collection<String> dlkeys = redisCache.keys(SoketEleKey.ELES_DATA + "*");
        for (String key : dlkeys) {
            //更新到具体单路里面
            flushPersistenceByKeyAndS(key);
        }
    }


    @Override
    public void flushPersistenceByMac(String mac) {
        String key = SoketEleKey.ELE_DATA + mac;
        flushPersistenceByKey(key);
        //多路更新
        String keys = SoketEleKey.ELES_DATA + mac;
        //更新到具体单路里面
        flushPersistenceByKeyAndS(keys);
    }

    @Override
    public List<SysDevCurrentDataRS> findListByrq(SysDevCurrentDataRq rq) {
        QueryWrapper queryWrapper = new QueryWrapper();
        if (rq.getAreaId() != null) {
            queryWrapper.eq("menu_id", rq.getAreaId());
             SysAreaInfo  sysAreaInfo = areaInfoMapper.selectOne(queryWrapper);
            if (sysAreaInfo != null ) {
                areaInfoService.findAllChild(sysAreaInfo);
            }
            sysAreaInfo.setChildIds(rq.getAreaId());
            rq.setAreaId(sysAreaInfo.getChildIds());
        }
        startPage();
        List<SysDevCurrentDataRS> currentDataRS
                = sysDevCurrentDataMapper.findListByrq(rq);
        if (currentDataRS != null && currentDataRS.size() > 0) {
            for (SysDevCurrentDataRS dataRS : currentDataRS) {
                replaceRealTime(dataRS);
                dataRS.setExStaus(redisCache.getCacheObject(SoketEleKey.EX_ENERY+dataRS.getDeviceId()));
            }
        }
        return currentDataRS;
    }

    @Override
    public  SysDevCurrentDataRS  findCacheByMac(String devid) {
        String online_key=SoketEleKey.ON_LINE+devid;
        if(redisCache.hasKey(online_key)){
            SysDevCurrentDataRS currentDataRS=new SysDevCurrentDataRS();
            currentDataRS.setDeviceId(devid);
            queryCaceByHomepage(currentDataRS);
            currentDataRS.setExStaus(redisCache.getCacheObject(SoketEleKey.EX_ENERY+currentDataRS.getDeviceId()));
            return currentDataRS;
        }
        return null;
    }

    @Override
    public SysDevCurrentData findByMac(String mac) {
        QueryWrapper queryWrapper=new QueryWrapper();
        queryWrapper.eq("device_id",mac);
        return sysDevCurrentDataMapper.selectOne(queryWrapper);
    }

    @Override
    public HomePageChartRS findHomePageCharts(DevInfoMacRq rq) {


        HomePageChartRS res = new HomePageChartRS(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO);

        if(StringUtils.isBlank(rq.getDevMac())) {
            return res;
        }

        LambdaQueryWrapper<SysDevInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysDevInfo::getDeviceId, rq.getDevMac());
        if(!SecurityUtils.isAdmin(SecurityUtils.getLoginUser().getUserId())){
            wrapper.eq(SysDevInfo::getSectionId, SecurityUtils.getLoginUser().getDeptId());
        }
        SysDevInfo sysDevInfo = sysDevInfoMapper.selectOne(wrapper);

        if(sysDevInfo != null) {
            // 读取缓存中该设备的实时数据
            String redisDataKey = SoketEleKey.ELE_DATA + rq.getDevMac();
            String redisData = redisCache.getCacheObject(redisDataKey);
            if(redisData == null){
                return res;
            }
            CurrentVo currentVo = JSON.parseObject(redisData, CurrentVo.class);
            // 加工为最终返回的数据
            generateHomePageChartRes(res, currentVo);
        }

        return  res;
    }

    /**
     * 加工实时数据
     * @param res
     * @param vo
     */
    private static void generateHomePageChartRes(HomePageChartRS res, CurrentVo vo) {

        // 平均功率
        if(vo.getActivePower() != null) {
            res.setAvgPowerFactor(res.getAvgPowerFactor().add(BigDecimal.valueOf(vo.getActivePower())));
        }

        if(vo.getVoltage() != null) {
            res.setAvgVoltage(res.getAvgVoltage().add(BigDecimal.valueOf(vo.getVoltage())));
        }
        if(vo.getCurrent() != null) {
            res.setElectricCurrent(res.getElectricCurrent().add(BigDecimal.valueOf(vo.getCurrent())));
        }
        if(vo.getFrequency() != null) {
            res.setAvgFrequency(res.getAvgFrequency().add(BigDecimal.valueOf(vo.getFrequency())));
        }

    }

    /**
     * 该方法是所有在线设备的
     * @return
     */
    @Override
    public HomePageChartRS findHomePageCharts() {
        QueryWrapper queryWrapper=new QueryWrapper();
        queryWrapper.eq("online_status",1);
        if(!SecurityUtils.isAdmin(SecurityUtils.getLoginUser().getUserId())){
            queryWrapper.eq("section_id",SecurityUtils.getLoginUser().getDeptId());
        }
        List<SysDevInfo> sysDevInfos=sysDevInfoMapper.selectList(queryWrapper);
        if(sysDevInfos!=null&&sysDevInfos.size()>0){
            List<String> devMac=sysDevInfos.stream().map(SysDevInfo::getDeviceId).collect(Collectors.toList());
            BigDecimal voltage = new BigDecimal(0);
            BigDecimal electricCurrent= new BigDecimal(0);
            BigDecimal frequency= new BigDecimal(0);
            BigDecimal powerFactor= new BigDecimal(0);
            int size=0;
            for(String mac:devMac){
                String redisDataKey = SoketEleKey.ELE_DATA + mac;
                String redisData = redisCache.getCacheObject(redisDataKey);
                if(redisData==null){
                    size++;
                    continue;
                }
                CurrentVo currentVo = JSON.parseObject(redisData, CurrentVo.class);
                if(currentVo.getVoltage()==null||currentVo.getCurrent()==null
                ||currentVo.getFrequency()==null||currentVo.getPowerFactor()==null){
                    size++;
                    continue;
                }
                voltage=  voltage.add(new BigDecimal( currentVo.getVoltage()));
                electricCurrent=  electricCurrent.add(new BigDecimal(currentVo.getCurrent()));
                frequency=  frequency.add(new BigDecimal(currentVo.getFrequency()));
                powerFactor=  powerFactor.add(new BigDecimal(currentVo.getPowerFactor()));
            }
            size=sysDevInfos.size()-size;
            if(size<=0){
                return  new HomePageChartRS();
            }
            voltage= voltage.divide(new BigDecimal(size),BigDecimal.ROUND_CEILING).setScale(2,BigDecimal.ROUND_UP);
            frequency= frequency.divide(new BigDecimal(size),BigDecimal.ROUND_CEILING).setScale(2,BigDecimal.ROUND_UP);
            powerFactor= powerFactor.divide(new BigDecimal(size),BigDecimal.ROUND_CEILING).setScale(2,BigDecimal.ROUND_UP);
            electricCurrent=electricCurrent.setScale(4,BigDecimal.ROUND_UP);
            return new HomePageChartRS(voltage,electricCurrent,frequency,powerFactor);
        }
        return  new HomePageChartRS();
    }

    @Override
    public void autoGenerationELHis() {
        QueryWrapper queryWrapper=new QueryWrapper();
        LocalDate currentDate = LocalDate.now();
        int year = currentDate.getYear();
        int month = currentDate.getMonthValue();
        int day = currentDate.getDayOfMonth();
        LocalDateTime dateTime = LocalDateTime.now();
        int hours=dateTime.getHour();
        queryWrapper.select("device_id");
        List<SysDevCurrentData> devlist= sysDevCurrentDataMapper.selectList(queryWrapper);
        if(devlist!=null&&devlist.size()>0){
            List<String> devMacs=devlist.stream().map(SysDevCurrentData::getDeviceId).collect(Collectors.toList());
            for (String devMac:devMacs){
                String key = SoketEleKey.ELE_DATA + devMac;
                AlmAutoEneraHis eneraHis=new AlmAutoEneraHis();
                eneraHis.setDevId(devMac);
                String redisData = redisCache.getCacheObject(key);
                if(redisData!=null&&!redisData.equals("")){
                    eneraHis.setOnlineStatus(1);
                    CurrentVo currentVo= JSON.parseObject(redisData, CurrentVo.class);
                    eneraHis.setActivePower(new BigDecimal(currentVo.getActivePower())
                            .setScale(4,BigDecimal.ROUND_DOWN));
                    eneraHis.setElectricCurrent(new BigDecimal(currentVo.getCurrent())
                            .setScale(4,BigDecimal.ROUND_DOWN));
                    eneraHis.setWorkStatus(currentVo.getWorkStatus());
                }else {
                    eneraHis.setOnlineStatus(0);
                    eneraHis.setWorkStatus(0);
                    eneraHis.setActivePower(new BigDecimal(0));
                    eneraHis.setElectricCurrent(new BigDecimal(0));
                }
                String exKey=SoketEleKey.EX_ENERY+devMac;
                if(redisCache.hasKey(exKey)){
                    eneraHis.setExStatus(1);
                }else{
                    eneraHis.setExStatus(0);
                }
                eneraHis.setAddTime(System.currentTimeMillis());
                eneraHis.setInfoNo(year+"-"+month+"-"+day+"  "+hours+"时");
                eneraHisMapper.insert(eneraHis);
            }
        }
    }

    @Override
    public byte[] get4GSliceUpData(String devId,String key, Integer thisPageNumbet) {
        List<String> send_data=SliceUpgradeUtil.UpgradeData;
        String send_str=send_data.get(thisPageNumbet);
        byte [] send_byte= ByteUtils.getByteArrByString(send_str);
        send_byte=get4GSliceUpData(send_data.size(),thisPageNumbet+1,send_byte);
        byte[] after= InstructionByteUtils.getBYTEISData(0x5743
                ,0x42,0xC0,
                send_byte,0x00);
        ByteBuf befor= InstructionByteUtils.getBFBYTEInstruction(devId,
                (short) 0x42,(short)0);
        byte [] inBytes= InstructionByteUtils.getBYTEISBYTES(befor,after);
        return inBytes;
    }

    private byte[] get4GSliceUpData(Integer total,Integer this_page,byte[] bytes){
        ByteBuf databuf = Unpooled.buffer(bytes.length+4);//拼接data
        databuf.writeByte(0x78);
        databuf.writeShort(total);
        databuf.writeShort(this_page);
        databuf.writeByte(0x55);
        databuf.writeByte(0x66);
        databuf.writeBytes(bytes);
        int crc16=CRC16.calculateCRC(bytes,bytes.length);
        databuf.writeByte(crc16&0xff);
        databuf.writeByte(crc16>>8);
        byte[]  array = new byte[databuf.readableBytes()];
        databuf.readBytes(array);
        databuf.release();
        return array;
    };
    private SysDevCurrentDataRS replaceRealTime(SysDevCurrentDataRS dataRS) {
        String redisDataKey = SoketEleKey.ELE_DATA + dataRS.getDeviceId();
        if (redisCache.hasKey(redisDataKey)) {
            String redisData = redisCache.getCacheObject(redisDataKey);
            CurrentVo currentVo = JSON.parseObject(redisData, CurrentVo.class);
            dataRS.setEnergy(currentVo.getEnergy() != null ? new BigDecimal(currentVo.getEnergy()).setScale(3, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setVoltage(currentVo.getVoltage() != null ? new BigDecimal(currentVo.getVoltage()).setScale(2, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setElectricCurrent(currentVo.getCurrent() != null ? new BigDecimal(currentVo.getCurrent()).setScale(3, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setActivePower(currentVo.getActivePower() != null ? new BigDecimal(currentVo.getActivePower()).setScale(3, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setFrequency(currentVo.getFrequency() != null ? new BigDecimal(currentVo.getFrequency()).setScale(2, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setPowerFactor(currentVo.getPowerFactor() != null ? new BigDecimal(currentVo.getPowerFactor()).setScale(2, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setRelayStatus(currentVo.getRelayStatus());
            dataRS.setWorkStatus(currentVo.getWorkStatus());
            dataRS.setTemperature(currentVo.getTemperature());
            dataRS.setModifyTime(currentVo.getRefreshTime());
            dataRS.setTotalEnergy(dataRS.getEnergy().add(dataRS.getTotalEnergyLast()));
        }
        return dataRS;
    }
    private SysDevCurrentDataRS queryCaceByHomepage(SysDevCurrentDataRS dataRS) {
        String redisDataKey = SoketEleKey.ELE_DATA + dataRS.getDeviceId();
        if (redisCache.hasKey(redisDataKey)) {
            String redisData = redisCache.getCacheObject(redisDataKey);
            CurrentVo currentVo = JSON.parseObject(redisData, CurrentVo.class);
            dataRS.setEnergy(currentVo.getEnergy() != null ? new BigDecimal(currentVo.getEnergy()).setScale(4, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setVoltage(currentVo.getVoltage() != null ? new BigDecimal(currentVo.getVoltage()).setScale(4, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setElectricCurrent(currentVo.getCurrent() != null ? new BigDecimal(currentVo.getCurrent()).setScale(4, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setActivePower(currentVo.getActivePower() != null ? new BigDecimal(currentVo.getActivePower()).setScale(4, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setFrequency(currentVo.getFrequency() != null ? new BigDecimal(currentVo.getFrequency()).setScale(4, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setPowerFactor(currentVo.getPowerFactor() != null ? new BigDecimal(currentVo.getPowerFactor()).setScale(4, BigDecimal.ROUND_CEILING) : new BigDecimal(0));
            dataRS.setRelayStatus(currentVo.getRelayStatus());
            dataRS.setWorkStatus(currentVo.getWorkStatus());
            dataRS.setTemperature(currentVo.getTemperature());
            dataRS.setModifyTime(currentVo.getRefreshTime());
        }
        return dataRS;
    }
    private void flushPersistenceByKey(String key) {
        String data = redisCache.getCacheObject(key);
        if (data != null && !data.equals("")) {
            CurrentVo currentVo = JSON.parseObject(data, CurrentVo.class);
            SysDevCurrentData currentData = ELeCurrentUtil.replaceData(currentVo);
            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.eq("device_id", currentData.getDeviceId());
            sysDevCurrentDataMapper.update(currentData, queryWrapper);
            redisCache.deleteObject(key);
        }
    }

    private void flushPersistenceByKeyAndS(String key) {
        String redisData = redisCache.getCacheObject(key);
        if (redisData != null && !redisData.equals("")) {
            Map map = JSON.parseObject(redisData, Map.class);
            for (Object currentVo1 : map.values()) {
                JSONObject jsonObject = (JSONObject) currentVo1;
                CurrentVo data = JSON.parseObject(jsonObject.toJSONString(), CurrentVo.class);
                SysDevCurrentData_s currentData = ELeCurrentUtil.replaceDataByS(data);
                QueryWrapper queryWrapper = new QueryWrapper();
                queryWrapper.eq("devid", currentData.getDevid());
                queryWrapper.eq("line_no", currentData.getLineNo());
                data_sMapper.update(currentData, queryWrapper);
            }
            redisCache.deleteObject(key);
        }
    }
}
